package com.yx.filesystem.storage;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Hashtable;
import java.util.Iterator;

import org.json.JSONArray;
import org.json.JSONObject;

public class SocketClient {
	public final static String UPLOAD_HANDLER = "com.yx.filesystem.storage.FileSetHandler";
	public final static String DOWNLOAD_HANDLER = "com.yx.filesystem.storage.FileGetHandler";
	public final static String STATE_HANDLER = "com.yx.filesystem.storage.StateHandler";
	public static final int SOCKET_TIME_OUT=1000*15;
	private java.net.Socket socket = null;
	private BufferedInputStream socketInput = null;
	private BufferedOutputStream socketOutput = null;
	
	public SocketClient() {
	}

	public boolean open(Node node) throws Exception {
		return open(node.getHost(), node.getPort());
	}

	public boolean open(String host, int port) throws Exception {
		socket = new Socket();
		socket.setSendBufferSize(SocketServer.SOCKET_BUFFER_SIZE); 
		socket.setReceiveBufferSize(SocketServer.SOCKET_BUFFER_SIZE); 
		socket.connect(new InetSocketAddress(host,port));
		socket.setTrafficClass(0x08|0x10);
		socketOutput = new BufferedOutputStream(socket.getOutputStream());
		socketInput = new BufferedInputStream(socket.getInputStream());
		return true;
	}

	public JSONObject send(String handler,JSONObject jsonContent) throws Exception {
		Transport jsonCmd = new Transport(handler, true, "", jsonContent);		
		jsonCmd.write(socketOutput);
		Transport resposeCmd = new Transport(socketInput);
		if (!resposeCmd.getStatus()) {
			throw new Exception(resposeCmd.getMessage());
		}
		return resposeCmd.getJsonContent();
	}

	public void getFile(Archive fileDoc) throws Exception {
		Transport fileGetCmd = new Transport(DOWNLOAD_HANDLER, true,
				fileDoc.getName(), null);
		fileGetCmd.write(socketOutput);
		Transport resposeCmd = new Transport(socketInput);
		if (!resposeCmd.getStatus()) {
			throw new Exception(resposeCmd.getMessage());
		}
		long fileLength=resposeCmd.getJsonContent().getLong("FILE_LENGTH");
		fileDoc.write(socketInput,fileLength);
	}

	public void getFile(Archive fileDoc, OutputStream outPut) throws Exception {
		BufferedOutputStream bos = null;
		try {
			bos = new BufferedOutputStream(outPut);
			Transport fileGetCmd = new Transport(DOWNLOAD_HANDLER, true,
					fileDoc.getName(), null);
			fileGetCmd.write(socketOutput);
			Transport resposeCmd = new Transport(socketInput);
			if (!resposeCmd.getStatus()) {
				throw new Exception(resposeCmd.getMessage());
			}
			long fileLength=resposeCmd.getJsonContent().getLong("FILE_LENGTH");
			byte[] buffer=new byte[SocketServer.BUFFER_SIZE];
			long remain = fileLength;
			while (remain>0) {
				if(remain<SocketServer.BUFFER_SIZE)buffer=new byte[Integer.parseInt(""+remain)];
				int size = socketInput.read(buffer);
				if (size > 0) {
					bos.write(buffer,0,size);
					bos.flush();
					remain=remain-size;
				}
			}
		} finally {
			try {
				bos.close();
			} catch (Exception e) {
			}
			try {
				outPut.close();
			} catch (Exception e) {
			}
		}
	}

	public String putFile(Archive fileDoc) throws Exception {
		JSONObject jsonContent=new JSONObject();
		jsonContent.put("FILE_LENGTH", fileDoc.length());
		Transport filePutCmd = new Transport(UPLOAD_HANDLER, true,
				fileDoc.getName(), jsonContent);
		filePutCmd.write(socketOutput);
		fileDoc.read(socketOutput);
		Transport resposeCmd = new Transport(socketInput);
		if (!resposeCmd.getStatus()) {
			throw new Exception(resposeCmd.getMessage());
		}
		return resposeCmd.getMessage();
	}

	public String putFile(InputStream file,long length) throws Exception {
		BufferedInputStream bis = null;
		try {
			JSONObject jsonContent=new JSONObject();
			jsonContent.put("FILE_LENGTH", length);
			Transport filePutCmd = new Transport(UPLOAD_HANDLER, false, "", jsonContent);
			filePutCmd.write(socketOutput);
			bis = new BufferedInputStream(file);
			byte[] contents = new byte[SocketServer.BUFFER_SIZE];
			int byteRead = 0;
			while ((byteRead = bis.read(contents)) != -1) {
				socketOutput.write(contents, 0, byteRead);
				socketOutput.flush();
			}
			Transport resposeCmd = new Transport(socketInput);
			if (!resposeCmd.getStatus()) {
				throw new Exception(resposeCmd.getMessage());
			}
			return resposeCmd.getMessage();
		} finally {
			try {
				bis.close();
			} catch (Exception ex) {
			}
			try {
				file.close();
			} catch (Exception ex) {
			}
		}
	}
	public State getStorageState(Node node) throws Exception{
		JSONObject jsonContent = new JSONObject();
		if(node!=null){
			jsonContent.put("NODE_ID", node.getId());
		}else{
			jsonContent.put("NODE_ID", "ALL_STATE");
		}		
		JSONObject returnObj = send(STATE_HANDLER,jsonContent);
		//System.out.println(returnObj.toString());
		JSONObject nodeState =returnObj.getJSONObject("NODE_STATE");
		Hashtable<String, BigInteger> storageSaveState=new Hashtable<String, BigInteger>();
		Iterator nodeKeys=nodeState.keys();
		while(nodeKeys.hasNext()){
			String nId=nodeKeys.next().toString();
			BigInteger value=new BigInteger(nodeState.getString(nId));
			storageSaveState.put(nId, value);
		}
		State state=new State();
		state.setFileState(storageSaveState);
		state.setServerId(returnObj.getString("NODE_ID"));
		state.setTotalSpace(returnObj.getLong("NODE_TOTALSPACE"));
		state.setUsedSpace(returnObj.getLong("NODE_USEDSPACE"));
		state.setFreeSpace(returnObj.getLong("NODE_FREESPACE"));
		state.setSpaceFull(returnObj.getBoolean("NODE_SPACE_FULL"));
		JSONArray backupArray=returnObj.getJSONArray("BACKUP_NODE");
		for(int i=0;i<backupArray.length();i++){
			state.addBackupNodes(backupArray.getString(i));
		}
		return state;
	}
	public void close() {
		try{
			if(socketInput!=null)socketInput.close();
		}catch(Throwable e){					
		}
		try{
			if(socketOutput!=null)socketOutput.close();
		}catch(Throwable e){					
		}
		try{
			if(socket!=null)socket.close();
		}catch(Throwable e){					
		}
	}

}
